//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <mantle.h>

// Interface functions
//
PInterface CSharedMemory::Probe(
    /* [in] */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(ISharedMemory *)this;
    }
    else if (riid == EIID_ISharedMemory) {
        return (ISharedMemory *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CSharedMemory;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_ISharedMemory;
    }
    return NULL;
}

UInt32 CSharedMemory::AddRef(void)
{
    return m_cRef.Increment();
}

UInt32 CSharedMemory::Release(void)
{
    Int32 lRef = m_cRef.Decrement();

    if (0 == lRef) {
        delete this;
    }
    return (UInt32)lRef;
}

ECode CSharedMemory::Aggregate(
    /* [in] */ AggregateType type,
    /* [in] */ PInterface pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CSharedMemory::GetDomain(
    /* [out] */ PInterface *ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CSharedMemory::Attach(
    /* [in] */ MemoryProtection protect,
    /* [out] */ Address * pVirtualAddress)
{
    Address address;
    MemoryOwner * pMemoryOwner;
    ECode ec = NOERROR;
    CProcess * pCPro;

    assert(m_baseAddr && m_nSize);

    if (NULL == pVirtualAddress || (protect & MemoryProtection_Exec)
        || (protect & IoRemapFlag_Direct)) {
        return E_INVALID_ARGUMENT;
    }

    pCPro = GetCurrentProcess();

    // Find if already attach to current process.
    m_memOwnerLock.Lock();
    pMemoryOwner = (MemoryOwner *)m_memoryOwnerList.m_pNext;
    while ((DLinkNode *)pMemoryOwner != &m_memoryOwnerList) {
        if (pMemoryOwner->m_pOwner == pCPro) {
            // Found it, just increase ref.
            pMemoryOwner->m_cRef += 1;
            *pVirtualAddress = m_baseAddr;
            break;
        }
        pMemoryOwner = (MemoryOwner *)pMemoryOwner->m_pNext;
    }

    // If not found, attach it.
    if ((DLinkNode *)pMemoryOwner == &m_memoryOwnerList) {
        ec = pCPro->m_AS.Map(
            m_baseAddr, m_nSize, MemoryMap_Fixed | MemoryMap_Shared, protect,
            this, 0, (UInt32*)&address);
        if (SUCCEEDED(ec)) {
            *pVirtualAddress = address;
            pMemoryOwner = new MemoryOwner;
            if (pMemoryOwner) {
                pMemoryOwner->m_pOwner = pCPro;
                pMemoryOwner->m_cRef = 1;
                m_memoryOwnerList.InsertFirst(pMemoryOwner);
                m_memOwnerLock.Unlock();
                ec = pCPro->AttachMemory(this);
            }
            else {
                m_memOwnerLock.Unlock();
                ec = E_OUT_OF_MEMORY;
            }
        }
        else {
            m_memOwnerLock.Unlock();
        }
    }
    else {
        m_memOwnerLock.Unlock();
    }

    return ec;
}

ECode CSharedMemory::Detach()
{
    assert(GetCurrentProcess());
    return Detach(GetCurrentProcess(), TRUE, FALSE);
}

// member functions
//
CSharedMemory::~CSharedMemory()
{
    MemoryOwner * pMemoryOwner;

    if (m_baseAddr && m_nSize) {
        // unmap all mapped memory of any process
        pMemoryOwner = (MemoryOwner *)m_memoryOwnerList.m_pNext;
        while ((DLinkNode *)pMemoryOwner != &m_memoryOwnerList) {
            pMemoryOwner->Detach();
            pMemoryOwner->m_pOwner->DetachMemory(this);
            pMemoryOwner->m_pOwner->m_AS.Unmap(m_baseAddr, m_nSize);
            delete pMemoryOwner;
            pMemoryOwner = (MemoryOwner *)m_memoryOwnerList.m_pNext;
        }
        assert(m_memoryOwnerList.IsEmpty());
        this->FreeGlobalAS();
    }
}

ECode CSharedMemory::AllocGlobalAS(Int32 nSize, Boolean bCommit)
{
    m_baseAddr = GlobalAS::AllocB(nSize);
    if (NULL == m_baseAddr) return E_OUT_OF_MEMORY;

    m_nSize = nSize;
    if (bCommit) {
        return m_cache.AllocPages(0, B_TO_P(m_nSize));
    }
    return NOERROR;
}

void CSharedMemory::FreeGlobalAS()
{
    GlobalAS::FreeB(m_baseAddr, m_nSize);
}

PPage * CSharedMemory::GetPage(UInt32 uOffset)
{
    PPage *pPage;

    if (uOffset >= (UInt32)m_nSize) return NULL;

    ForEachDLinkNode(PPage *, pPage, &m_cache.m_pagesList) {
        if (FALL_INTO(uOffset, pPage->m_uOffset, pPage->m_uSize)) {
            return pPage;
        }
    }
    return m_cache.CreatePage(AlignPageDown(uOffset), 0);
}

ECode CSharedMemory::Flush()
{
    return NOERROR;
}

ECode CSharedMemory::Detach(
    CProcess * pCPro, Boolean NeedProcDetach, Boolean Force)
{
    ECode ec = NOERROR;
    MemoryOwner * pMemoryOwner;

    assert(m_baseAddr && m_nSize);

    // Find if attach to current process.
    m_memOwnerLock.Lock();
    pMemoryOwner = (MemoryOwner *)m_memoryOwnerList.m_pNext;
    while ((DLinkNode *)pMemoryOwner != &m_memoryOwnerList) {
        if (pMemoryOwner->m_pOwner == pCPro) {
            // Found it
            if (!Force && pMemoryOwner->m_cRef > 1) {
                pMemoryOwner->m_cRef -= 1;
                m_memOwnerLock.Unlock();
            }
            else {
                assert(Force || pMemoryOwner->m_cRef == 1);
                ec = pCPro->m_AS.Unmap(m_baseAddr, m_nSize);
                if (SUCCEEDED(ec)) {
                    pMemoryOwner->Detach();
                    m_memOwnerLock.Unlock();
                    delete pMemoryOwner;
                    if (NeedProcDetach) pCPro->DetachMemory(this);
                }
                else {
                    m_memOwnerLock.Unlock();
                }
            }
            break;
        }
        pMemoryOwner = (MemoryOwner *)pMemoryOwner->m_pNext;
    }

    // if not found, invalid operation.
    if ((DLinkNode *)pMemoryOwner == &m_memoryOwnerList) {
        m_memOwnerLock.Unlock();
        ec = E_INVALID_OPERATION;
    }

    return ec;
}
